using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Net.Security;
using System.Security.Authentication;
using System.IO;
using System.Diagnostics;

namespace AdaNNTP
{
	public class SegmentDownloader : TcpClient
	{
		public NNTPServer targetServer = new NNTPServer();
        public Segment segmentToDownload;
        
		
		private Stream serverStream;
		
		public delegate void OnDownloadCompleteDelegate(string downloadText);
        public event OnDownloadCompleteDelegate OnDownloadComplete;

        public delegate void OnDownloadFailedDelegate(Segment segmentToDownload);
        public event OnDownloadFailedDelegate OnDownloadFailed;

        public SegmentDownloader(Segment segmentToDownloadRef)
        {
            segmentToDownload = segmentToDownloadRef;
        }
		
		public void SegmentDownload ()
		{
			if(NewsgroupService.Instance.Active)
			{

                //++(NewsgroupService.Instance.serverConnectionCount[targetServer.serverAddress]);
                //Debug.Assert(NewsgroupService.Instance.serverConnectionCount[targetServer.serverAddress] < 21);
				base.Connect(targetServer.serverAddress, targetServer.serverPort);

				segmentToDownload.Active = true;
				serverStream = GetStream();
				
				StreamReader serverReader = new StreamReader(serverStream, System.Text.Encoding.GetEncoding("iso-8859-1"));
				StreamWriter serverWriter = new StreamWriter(serverStream);
                serverWriter.AutoFlush = true;
				
				//If we don't get a 200-series response, then the connection is bad, and we need to abort
				if(!serverReader.ReadLine().StartsWith("20"))
					throw new SocketException();

                if (targetServer.UseLogon)
                {
                    try
                    {
                        //Authenticate with the server
                        AuthenticateUser(targetServer.Username, targetServer.Password);
                    }
                    catch
                    {
                        if (OnDownloadFailed != null)
                        {
                            OnDownloadFailed(segmentToDownload);
                        }
                        
                        this.Dispose(true);
                        return;
                    }
                }

                //Tell the server we want to 'read'
                serverWriter.WriteLine("MODE READER");
                string modeResponse = serverReader.ReadLine();

                if (targetServer.NeedsGroup)
                {
                    serverWriter.WriteLine("GROUP " + segmentToDownload.Group);
                    string groupResponse = serverReader.ReadLine();
                }

                serverWriter.WriteLine("BODY <" + segmentToDownload.MessageID + ">");
                string tempResponse = serverReader.ReadLine();
                if (tempResponse.StartsWith("22"))
                {
                    bool Done = false;
                    string response;
                    StringBuilder Body = new StringBuilder(256 * 1024);
                    do
                    {

                        response = serverReader.ReadLine();


                        if (response == ".")
                        {
                            Done = true;
                        }
                        else
                        {
                            if (response.StartsWith(".."))
                                response = response.Remove(0, 1);

                            Body.Append(response);
                            Body.Append("\r\n");
                        }
                    } while (!Done);

                    serverWriter = null;
                    serverReader.Close();

                    segmentToDownload.Active = false;
                    segmentToDownload.Contents = Body.ToString();
                    segmentToDownload.Completed = true;

                    //(NewsgroupService.Instance.serverConnectionCount[targetServer.serverAddress])--;
                    if (OnDownloadComplete != null)
                        OnDownloadComplete(Body.ToString());
                }
                else
                {
                    if (OnDownloadFailed != null)
                        OnDownloadFailed(segmentToDownload);
                }
            }
			
		}
		
		public new Stream GetStream()
		{

			if (serverStream == null)
			{

				if (!targetServer.UseSSL)
				{
					serverStream = base.GetStream();
				}
				else
				{
					
					SslStream secureStream = new SslStream(base.GetStream(), true);

					try
					{
						secureStream.AuthenticateAsClient(targetServer.serverAddress);
						serverStream = secureStream;

					}
					catch /*(AuthenticationException e)*/
					{
						serverStream = null;
					}

				}
			}

			return serverStream;
		}
		
		/// <summary>
		/// Authenticate user with connected server.
		/// </summary>
		/// <param name="username">Username</param>
		/// <param name="password">Password</param>
		public void AuthenticateUser(string username, string password)
		{
			string response;

			Stream temporaryServerStream = GetStream();
			StreamWriter serverWriter = new StreamWriter(temporaryServerStream);
			StreamReader serverReader = new StreamReader(temporaryServerStream);

			if(serverWriter == null || serverReader == null)
				throw new Exception("Stream could not be opened.");

			serverWriter.AutoFlush = true;

			serverWriter.WriteLine("AUTHINFO USER " + username);
			response = serverReader.ReadLine();

			serverWriter.WriteLine("AUTHINFO PASS " + password);
			response = serverReader.ReadLine();

			if(response.StartsWith("482") || response.StartsWith("502"))
                throw new AuthenticationException("Authentication failed. Connections: " + NewsgroupService.Instance.serverConnectionCount[targetServer.serverAddress].ToString() + @"/" + targetServer.MaxConnectionCount.ToString());

			if(!response.StartsWith("281"))
			{
				//Attempt generic authentication
				serverWriter.WriteLine( "AUTHINFO GENERIC " + username + " " + password);
				response = serverReader.ReadLine();
				
				if(response.StartsWith("502"))
					throw new AuthenticationException("Authentication failed");
				
				if(!response.StartsWith("281"))
				{
					//Attempt simple authentication
					serverWriter.WriteLine("AUTHINFO SIMPLE");
					response = serverReader.ReadLine();
					serverWriter.WriteLine(username + password);
					response = serverReader.ReadLine();

					if(response.StartsWith("452"))
						throw new AuthenticationException("All authentication methods failed");
					if(!response.StartsWith("250"))
					{
					}
					else
					{
						return;
					}
				}
				else
				{
					return;
				}
			}
			else
			{
				return;
			}

			Console.WriteLine("Client failed to authenticate with server.");
			throw new Exception(response);
		}

        public new void Dispose(bool disposing)
        {
            segmentToDownload = null;
            targetServer = null;

            serverStream = null;
            base.Dispose(disposing);
        }
	}
	

}

